//设计模式是一种编程的思想，可根据不同需求选择不同的编程思路。
//高内聚:一个函数只负责处理单一职责功能，就代码联系密切。


//代理模式: new Proxy(obj,{get(target,key),set(target,key,value)})




/*观察者模式(重要)
观察者模式包含 观察目标subject 和观察者observer 两类对象
一个目标可以有任意多个观察者,可以添加或删除观察者,可以通知观察者。
一旦观察目标属性/状态发生变化，所有观察者可以得到通知。

优点：方便建立一对多依赖关系，可实现观察和被观察者的代码解耦(及低耦合：关联性低，目标统一，但可以联系起来)
缺点：如果关联的观察者太多，会直接或间接通知所有观察者，会比较耗费时间，不能进行细分管控。
*/
class Subject{
   constructor(){
      this.observers = [];
   }   

   add(observer){
       this.observers.push(observer);
   }

   remove(observer){
      let a = this.observers.indexOf(observer);
      this.observers.splice(a,1)
   }

   notify(){  //通知和更新方法都固定了，不够灵活，不能细分
       this.observers.forEach(item=>{
          item.update();
      })
   }
}


class Observer{
   constructor(message){
     this.message = message;
   }

   update(){
     console.log(this.message)
   }
}

// let subject = new Subject();
// let observer1 = new Observer('1');
// let observer2 = new Observer('2');

// subject.add(observer1)
// subject.add(observer2)

// subject.notify()
// subject.remove(observer2)
// subject.notify()

//观察者模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>







/*装饰器模式（灵活,自由，可扩展）
把代码的某些功能抽离出来用于复用,如异常处理，数据统计(用户行为),ajax加token
就是装饰品一样，需要就挂上，不要就去掉，而不是一开始就固定写好

比如nest里提供很多装饰器,比如类装饰器、方法装饰器、属性装饰器...（加上装饰器后，可以修改类、方向的）
缺点: 实现起来比较麻烦,一开始的学习心智负担较大。
*/

Function.prototype.before = function(beforeFn){
  let _this = this; //test执行了before,所以这里this = test
  return function(){
    //前置函数调用
    beforeFn.apply(this,arguments);  //在调用前置函数时候，会把原来的参数传给beforeFn
    //执行原来的函数
    return _this.apply(this,arguments);
  }
}


Function.prototype.after = function(afterFn){
  let this_ = this;

  return function(){
     this_.apply(this,arguments);
     return afterFn.apply(this,arguments);
  }
}

//demo1
const test = function(arg){
console.log(arg)
}
const test1 = test.before(()=>{ console.log("0000"); })
         .after(()=>{ console.log("2222"); })


//demo2
function ajax(url,post,params){
console.log(url,post,params);
}

//在执行ajax之前,先修改params参数,因为此时this和argumens都是ajax的数据,所以可以接收到
const ajax_ = ajax.before((url,post,params)=>{
params.token = 'abcdefghijklmnopqrstuvwxyz';
});


//加token  | /api POST { name: 'newking', token: 'abcdefghijklmnopqrstuvwxyz' }
ajax_("/api","POST",{
name: "newking"
})


//不加token | /api POST { name: 'newking' }
ajax("/api","POST",{
name: "newking"
})
//装饰器模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>








/*单例模式
优点：永远返回同一个实例，首次会被创建，之后调用会返回第一次创建的实例。主要解决重复创建销毁所带来的性能和内存消耗。
     1、通过闭包创建
*/
//闭包
const Singleton = (function(){
  let instance;

  function User(name,age){
     this.name = name;
     this.age  = age;
  }


  return function(name,age){
    if(!instance){
        instance = new User(name,age);
    }
    return instance;
  }

})()
let sing = Singleton('zjh',25)


//类
class Singleton_{
 constructor(name,age,mes){
    if(!Singleton_.instance){
      this.name = name;
      this.age  = age;
      Singleton_.instance = this;
    }
    return Singleton_.instance; 
 }
}
let sing_ = new Singleton_('zjh',18)
//单例模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>










/*构造器模式
优点：不用重复声明属性,方便维护和拓展。
缺点：方法逻辑一般都是可以共用的，而构造器模式每次创建实例都需要多开辟一个空间来存储方法。
*/


/*原型模式
优点：方法挂在原型上，实例可以访问到，节省内存占用。
缺点：不够灵活，不能有自己独立的方法，修改原型的时候会影响到其他对象。
*/




/*工厂模式
不直接创建函数或实例，而是通过某个方法来间接创建。比较灵活，比如做权限管理的时候，可以判断用户身份，返回不同的实例对象。
缺点：不适合大型应用，类别和要传的参数太多不太方便维护，比如用swith判断,属性或者参数一旦有变动，要修改很多代码。
*/
/*抽象工厂
提供一个基类，把不共用属性方法的抽离出来继承基类。(更方便维护和拓展)
再通过一个工厂函数，得到相关的类指针。
*/




/*适配器模式
可以类某些方法不适配的问题，不同类功能相同但方法名不同导致，最后在聚合的时候出现不统一的问题。
因为第三方类,一般不会去改动代码,所以可以通过继承，统一调用接口(而不是在调用时候判断，多了会导致结构不清晰不利于代码维护)
*/

class TencentMap{
   show(){
     console.log('腾讯地图');
   }
}

class BaiduMap{
   display(){
     console.log('百度地图');
   }
}

//开始适配
class TencentMapAdapter extends TencentMap{
    constructor(){ super() }
    display(){
      this.show();
    }
}


//聚合方法
function MapRender(map){
   map.display();
}

MapRender(new TencentMapAdapter);
MapRender(new BaiduMap);
//适配器模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>





/*策略模式
通过对象匹配，减少if/else判断
*/
let list = [
   {
    title: "发高烧，人在医院",
    type: 0
   },
   {
    title: "太困了,不想上班",
    type: 1,
   },
   {
    title: "家里有急事,得回去一趟",
    type: 2
   }
]

myListCategory = {
    0: {
      content: "审核中",
      className : "wait"
    },
    1: {
      content: "被驳回",
      className: "warn"
    },
    2: {
      content: "已通过",
      className: "success"
    }
}

let liststr = list.map( (item) => {
   return `
       <li>
         <div>${item.title}</div>
         <div class="${myListCategory[item.type].className}">${myListCategory[item.type].content}</div>
       </li>
   `
  
  //  if(item.type === 0){
  //     xxx
  //  }
  //  if(item.type === 2)...
}).join("")
// console.log(liststr);
// xxx.innerHTML = liststr;
//策略模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>




/*
迭代器模式: 要处理一份可迭代数据的时候,可以把调用和处理逻辑解耦，将调用逻辑和处理逻辑分开
*/

const myEach = (arr,cb)=>{
     arr.forEach((item,key)=>{
         cb(item,key)
     })
}


myEach([1,2,3],(item,key)=>{
  console.log(key,item)
})


//迭代器复习
let arr = [1,2,3];
let it = arr[Symbol.iterator]();
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

let obj = {
   0:1,
   1:2,
   2:3,
   length: 3,
  //  [Symbol.iterator]:Array.prototype[Symbol.iterator]
  [Symbol.iterator]:function(){
     
     let index = 0;
     return {
       next(){
         return {
           value: obj[index],                 //obj[index++]
           done: index++ && index>obj.length  //index > obj.length
         }
       }
     }
  }
}

//for of 会自动调用[Symbol.iterator]():obj,中的next方法,当返回的{value:xxx,done:true/false}中,done为true时，会自动停止遍历
for(let i of obj){
  console.log(i);
}


//迭代器模式结束>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>





/*其他
建造者模式：不直接通过实例去调用方法，而是通过一个函数，传一个实例对象，在函数中统一调用
NestJs 很多复杂对象都用到了,有时间得补一补
*/